////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef __WN_NETWORKING_NETWORK_MANAGER_H__
#define __WN_NETWORKING_NETWORK_MANAGER_H__

#include "WNCore/inc/WNPlatform.h"
#include "WNCore/inc/WNTypes.h"
#include "WNConcurrency/inc/WNCriticalSection.h"
#include "WNConcurrency/inc/WNThread.h"
#include "WNNetworking/inc/WNNetworkReadBuffer.h"
#include "WNNetworking/inc/WNNetworkWriteBuffer.h"
#include "WNContainers/inc/WNCallback.h"
#include "WNContainers/inc/WNResourcePointer.h"

#include <deque>
#include <vector>
#include <map>

namespace WNNetworking {
    enum WNCallbackMessage {
        eWNCallbackNone,
        eWNCallbackListener
    };

    enum WNConnectionType {
        eWNReliable,
        eWNUnreliable
    };

    enum WNNetworkingError {
        #include "WNCore/inc/WNErrors.h"
        WN_NETWORKING_MANAGER_TOO_MANY_THREADS,
        WN_NETWORKING_MANAGER_SYSTEM_INITIALIZATION_FAILED,
        WN_NETWORKING_MANAGER_THREAD_CREATION_ERROR,
        WN_NETWORKING_MANAGER_CANNOT_CREATE_SOCKET,
        WN_NETWORKING_MANAGER_CANNOT_LISTEN,
        WN_NETWORKING_MANAGER_BAD_HOSTNAME,
        WN_NETWORKING_MANAGER_CANNOT_CONNECT,
        WN_NETWORKING_MANAGER_CANNOT_ASSOCIATE
    };

    class WNNetworkWriteBuffer;

    typedef WN_ATOM_T WNConnectionHandle;

    static const WN_UINT32 WNInvalidNetworkHandle = 0xFFFFFFFF;

    class WNNetworkManager {
    public:
        WNNetworkManager();
        WNNetworkingError Initialize(WN_UINT32 _numWorkerThreads);
        WNNetworkingError ConnectTo(WNConnectionHandle& _outHandle, WNConnectionType _type, const WN_CHAR* _target, WN_SIZE_T _port);
        WNNetworkingError CreateListener(WNConnectionHandle& _outHandle, WNConnectionType _type, WN_UINT16 _port, WNCallback2<WN_BOOL, WNConnectionHandle, const WN_CHAR*> _callback);
        WNNetworkingError CreateConnectionGroup(WNConnectionHandle& _outHandle, const WN_CHAR* _groupName);
        WNNetworkingError AddToGroup(WNConnectionHandle _handle, WNConnectionHandle _group); 
        WNNetworkingError SendBuffer(WNConnectionHandle _handle, WNNetworkWriteBuffer& buffer);
        WN_VOID InitializeBuffer(WNNetworkWriteBuffer& _buffer, WN_UINT32 _number);
        WN_VOID SetCallback(WN_UINT32 _identifier, WNCallback2<WN_VOID, WNConnectionHandle, WNNetworkReadBuffer&> _buffer);
        WNCallback2<WN_VOID, WNConnectionHandle, WNNetworkReadBuffer&>* GetCallback(WN_UINT32 _identifier);
        WN_VOID Cleanup();
    private:
        enum SocketType{
            eWNSocketServer,
            eWNSocketFromClient,
            eWNSocketClient,
            eWNSocketGroup
        };

        struct Group {
            explicit Group(WN_CHAR* _groupName): mGroupName(_groupName) {}
            explicit Group(const Group& _other): 
                mGroupName(_other.mGroupName),
                mGroupedHandles(_other.mGroupedHandles){
            }

            const WN_CHAR* mGroupName;
            std::vector<WNConnectionHandle> mGroupedHandles;
        };

        static const WN_UINT32 CONNECTION_MASK = 0x00000003;
        static const WN_UINT8  CONNECTION_SHIFT = 30;
        class WNConnectionData;

        std::map<WNConnectionHandle, WNConnectionData*> mHandles;
        std::map<WNConnectionHandle, Group*> mGroups;
        std::map<WN_UINT32, WNCallback2<WN_VOID, WNConnectionHandle, WNNetworkReadBuffer&> > mCallbacks;

        WNConcurrency::WNCriticalSection mConnectionLock;
        WN_UINT32 mMaxThreads;
        std::vector<WNConcurrency::WNThread*> mThreads;
        WNConcurrency::WNThread mListenThread;
        std::vector<WNContainers::WNResourcePointer<WNBufferResource> > mRecycledChunks;
        WNConcurrency::WNCriticalSection mRecycleLock;
        WN_ATOM_T mCurrentHandleVal;

        #ifdef _WN_WINDOWS
            #include "WNNetworking/inc/WNNetworkingWindowsHeader.h"
        #else 
            #include "WNNetworking/inc/WNNetworkingLinuxHeader.h"
        #endif
    };
}

#endif // __WN_NETWORKING_NETWORK_MANAGER_H__